package com.um.jdy.app.charge.service.impl;

import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil;
import com.github.binarywang.wxpay.bean.payscore.*;
import com.github.binarywang.wxpay.exception.WxPayException;
import com.github.binarywang.wxpay.service.WxPayService;
import com.um.jdy.app.charge.manager.PBOrderManager;
import com.um.jdy.app.charge.service.PayScoreService;
import com.um.jdy.common.charge.entity.po.DepositOrder;
import com.um.jdy.common.charge.enums.OrderEnum;
import com.um.jdy.common.charge.service.DepositOrderService;
import com.um.jdy.common.charge.service.OrderService;
import com.um.jdy.common.device.service.FeePbPriceService;
import com.um.springboot.starter.exception.ServiceException;
import com.um.springboot.starter.properties.HshProperties;
import com.um.springboot.starter.utils.StringUtil;
import com.um.weixin.pay.config.WxPayProperties;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Date;
import java.util.Map;

/**
 * @author renjunwei
 * @since 2023/1/26
 * 版权所有 广州优匠科技有限公司
 */
@Service
@Transactional
@Slf4j
public class WxPayScoreServiceImpl implements PayScoreService {

    @Autowired
    OrderService orderService;

    @Autowired
    PBOrderManager PborderManager;

    @Autowired
    WxPayService wxService;

    @Autowired
    WxPayProperties wxPayProperties;

    @Autowired
    HshProperties hshProperties;

    @Autowired
    FeePbPriceService feePbPriceService;

    @Autowired
    DepositOrderService depositOrderService;

    /**
     * 创建支付分订单
     * @return
     */
    public Map create(String outTradeNo) {


        TimeRange timeRange = new TimeRange();
        timeRange.setStartTime("OnAccept"); //用户确认订单成功即开始服务

        RiskFund riskFund = new RiskFund();
        riskFund.setName("ESTIMATE_ORDER_COST"); //信用分评估不通过不可使用服务
        riskFund.setAmount(10000); //风险金额
        riskFund.setDescription("充电宝租借预估费用"); //风险说明

        WxPayScoreRequest req = WxPayScoreRequest.builder()
                .appid(wxPayProperties.getAppId())
                .outOrderNo(outTradeNo)
                .serviceId(wxPayProperties.getServiceId())
                .serviceIntroduction("充电宝租借") //服务信息
                .timeRange(timeRange) //服务时间段
                .riskFund(riskFund) //订单风险金
                .notifyUrl(StrUtil.builder(hshProperties.getConfig().getDomain(), "/api/v1/order/wxpay_score/notify").toString())
                .needUserConfirm(true)
                .build();

        WxPayScoreResult result;
        try {
            result = wxService.getPayScoreService().createServiceOrder(req);
        } catch (WxPayException e) {
            log.error("创建微信支付分订单出错", e);
            throw new ServiceException("创建微信支付分订单出错");
        }

        log.info("创建微信支付分订单结果：{}", cn.hutool.json.JSONUtil.toJsonStr(result));

        if(null == result || "CREATED".equals(result.getState())){
            throw new ServiceException("创建微信支付分订单出错");
        }

        return result.getPayScoreSignInfo();
    }


    /**
     * 支付分订单查询
     *
     * @param orderNo
     * @return
     */
    @Override
    public Map query(String orderNo) {
        //先在数据库查询是否确认微信支付分订单。如果微信还未通知我们，此时再去调用微信接口主动查询支付分订单
        DepositOrder depositOrder = depositOrderService.getEntity("order_no", orderNo);
        if(null == depositOrder){
            throw new ServiceException("微信支付分订单不存在");
        }

        //信用分不足，通知前端需充值押金
        if(StringUtil.equals(depositOrder.getPayStatus(), OrderEnum.PayScoreStatus.NotAvailable)){
            return MapUtil.of("need_recharge_deposit", true);
        }

        if(!StringUtil.equals(depositOrder.getPayStatus(), OrderEnum.PayScoreStatus.Created)){
            return MapUtil.of("need_recharge_deposit", StringUtil.equals(depositOrder.getPayStatus(), OrderEnum.PayScoreStatus.Canceled));
        }

        String outTradeNo = depositOrder.getOutTradeNo();

        //主动去微信查询订单确认状态
        WxPayScoreResult result;
        try {
            result = wxService.getPayScoreService().queryServiceOrder(outTradeNo, null);
        } catch (WxPayException e) {
            log.error("查询微信支付分订单出错", e);
            throw new ServiceException("查询微信支付分订单出错");
        }

        log.info("查询微信支付分订单结果：{}", cn.hutool.json.JSONUtil.toJsonStr(result));

        if(null == result){
            throw new ServiceException("微信支付分订单不存在");
        }

        OrderEnum.PayScoreStatus status = null;

        //未确认，取消微信支付分订单
        if("CREATED".equals(result.getState())){
            status = OrderEnum.PayScoreStatus.Canceled;

            try {
                wxService.getPayScoreService().cancelServiceOrder(result.getOutOrderNo(), "超时未确认");
            } catch (WxPayException e) {
                log.error("取消微信支付分订单出错", e);
                throw new ServiceException("取消微信支付分订单出错");
            }
        }

        //已确认，开启租借充电宝
        else if("DOING".equals(result.getState()) && "USER_CONFIRM".equals(result.getStateDescription())){
            status = OrderEnum.PayScoreStatus.InService;

            String code = depositOrder.getCode();
            String userId = depositOrder.getMemberUserId();
            PborderManager.createChargeOrder(orderNo, outTradeNo, code, userId, OrderEnum.CreditType.WxPayScore.name());
        }

        //已完结

        if(null != status){
            depositOrder.setPayStatus(status.name());
            depositOrderService.save(depositOrder);
        }

        return MapUtil.of("need_recharge_deposit", OrderEnum.PayScoreStatus.Canceled == status);
    }

    /**
     * 支付分订单通知
     *
     * @param notifyData
     * @return
     */
    @Override
    public void notify(PayScoreNotifyData notifyData) {
        log.info("微信支付分订单确认通知：{}", cn.hutool.json.JSONUtil.toJsonStr(notifyData));

        WxPayScoreResult result;
        try {
            result = wxService.getPayScoreService().decryptNotifyDataResource(notifyData);
        } catch (WxPayException e) {
            throw new RuntimeException(e);
        }

        log.info("微信支付分订单确认通知解密：{}", cn.hutool.json.JSONUtil.toJsonStr(result));

        if(null == result){
            throw new ServiceException("微信支付分订单确认通知解密失败");
        }

        DepositOrder depositOrder = depositOrderService.getEntity("out_trade_no", result.getOutOrderNo());
        if(null == depositOrder){
            throw new ServiceException("微信支付分订单不存在");
        }

        if("PAYSCORE.USER_CONFIRM".equals(notifyData.getEventType())){
            depositOrder.setPayScoreStatus(OrderEnum.PayScoreStatus.InService.name());
            depositOrderService.save(depositOrder);
            PborderManager.createChargeOrder(depositOrder.getOrderNo(), result.getOutOrderNo(), depositOrder.getCode(),
                    depositOrder.getMemberUserId(), OrderEnum.CreditType.Deposit.name());

        } else if ("PAYSCORE.USER_PAID".equals(notifyData.getEventType())) {
            depositOrder.setPayScoreStatus(OrderEnum.PayScoreStatus.DeductionComplete.name());
            depositOrderService.save(depositOrder);

//            PborderManager.wechatPayCallback(depositOrder.getOutTradeNo(), null, DateUtil.date()); TODO
        }

    }

    /**
     * 完结支付分订单
     *
     * @param outTradeNo
     * @param amount
     */
    @Override
    public void complete(String outTradeNo, int amount) {
        DepositOrder depositOrder = depositOrderService.getEntity("out_trade_no", outTradeNo);
        if(null == depositOrder){
            throw new ServiceException("微信支付分订单不存在");
        }

        PostPayment postPayment = new PostPayment();
        postPayment.setName("充电宝租借");
        postPayment.setAmount(amount);

        WxPayScoreRequest req = WxPayScoreRequest.builder()
                .appid(wxPayProperties.getAppId())
                .serviceId(wxPayProperties.getServiceId())
                .outOrderNo(outTradeNo)
                .totalAmount(amount) //总金额
                .postPayments(ListUtil.toList(postPayment)) //后付费项目
                .build();

        WxPayScoreResult result;
        try {
            result = wxService.getPayScoreService().completeServiceOrder(req);
        } catch (WxPayException e) {
            log.error("完结支付分订单出错", e);
            throw new ServiceException("完结支付分订单出错");
        }

        log.info("完结支付分订单结果：{}", cn.hutool.json.JSONUtil.toJsonStr(result));

        depositOrder.setPayScoreStatus(OrderEnum.PayScoreStatus.Finished.name());
        depositOrderService.save(depositOrder);
    }

    /**
     * 同步服务订单信息
     *
     * @param outTradeNo
     * @param payTime
     */
    @Override
    public void sync(String outTradeNo, Date payTime) {
        Detail detail = new Detail();
        detail.setPaidTime(DateUtil.format(payTime, DatePattern.PURE_DATETIME_PATTERN));

        WxPayScoreRequest req = WxPayScoreRequest.builder()
                .appid(wxPayProperties.getAppId())
                .serviceId(wxPayProperties.getServiceId())
                .outOrderNo(outTradeNo)
                .type("Order_Paid") //场景类型为“Order_Paid”，字符串表示“订单收款成功”
                .detail(detail) //内容信息详情
                .build();

        WxPayScoreResult result;
        try {
            result = wxService.getPayScoreService().syncServiceOrder(req);
        } catch (WxPayException e) {
            log.error("同步服务订单信息出错", e);
            throw new ServiceException("同步服务订单信息出错");
        }

        log.info("同步服务订单信息结果：{}", cn.hutool.json.JSONUtil.toJsonStr(result));

        // TODO 更新信用付订单状态， 目前还不知道微信返回的状态时啥
    }
}
